package yicr.untils;

import cn.hutool.core.util.StrUtil;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import yicr.model.NslPointInt;
import yicr.model.excel.CellTypeAndValue;
import yicr.model.excel.ExcelStringRowTable;
import yicr.model.excel.ExcelTemplate;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.*;

public class NslUtilExcelCommonUtils {

    //todo:找出 列最大数，行最大数

    /**
     * 在指定行中，找出 最右侧的，不为空的列的 index
     *
     * @return
     */
    public static int getUserMaxColumnIndex(Sheet sheet, int beginRowIndex, int endRowIndex) {
        int maxColumnIndex = 0;
        for (int i = beginRowIndex; i <= endRowIndex; i++) {
            Row row = sheet.getRow(i);
            int index = row.getLastCellNum() - 1;
            if (index >= maxColumnIndex) {
                maxColumnIndex = index;
            }
        }
        return maxColumnIndex;
    }

    public static Map<String, Integer> getMapOfSheetNameAndIndex(Workbook workbook) {
        int size = workbook.getNumberOfSheets();
        Map<String, Integer> nameAndIndexMap = new HashMap<>();
        for (int i = 0; i < size; i++) {
            Sheet sheet = workbook.getSheetAt(i);
            nameAndIndexMap.put(sheet.getSheetName(), i);
        }
        return nameAndIndexMap;
    }

    /**
     * 删除sheet中所有的 合并区域，并返回删除的合并区域list
     *
     * @param sheet
     * @return
     */
    static public List<CellRangeAddress> removeAllMergedRegions(Sheet sheet) {
        List<CellRangeAddress> cellRangeAddressList = sheet.getMergedRegions();
        int sheetMergeCount = sheet.getNumMergedRegions();
        List<Integer> removeList = new ArrayList<>();
        for (int i = 0; i < sheetMergeCount; i++) {
            removeList.add(i);
        }
        sheet.removeMergedRegions(removeList);
        return cellRangeAddressList;
    }

    /**
     * 将 cellAddresses 区域内的cells ,都赋值为 offset 0,0 的值
     *
     * @param cellAddresses
     * @param sheet
     */
    static public void fillCellRangeAddress(CellRangeAddress cellAddresses, Sheet sheet) {
        int rowBegin = cellAddresses.getFirstRow();
        int rowEnd = cellAddresses.getLastRow();
        int columnBegin = cellAddresses.getFirstColumn();
        int columnEnd = cellAddresses.getLastColumn();
//        log.info("rowBegin/rowEnd/columnBegin/columnEnd = {}/{}/{}/{}",rowBegin,rowEnd,columnBegin,columnEnd);
        String content = sheet.getRow(rowBegin).getCell(columnBegin).getStringCellValue();
        System.out.println(content);
        for (int i = rowBegin; i <= rowEnd; i++) {
            for (int j = columnBegin; j <= columnEnd; j++) {

                Cell cell = sheet.getRow(i).getCell(j);

//                log.info("cell = {}",cell);
                cell.setCellValue(content);
            }
        }
    }


    /**
     * * 插入新行，以字符串的方式拼接 该列指定的 多行内容
     * 该行每列的值=textJoin(beginRowIndex:lastRowIndex,"_")
     * 比如 a0:1 a1:2  现在在rowIndex5的地方插入行 a5=1_2,
     * 这样操作的范围：由beginColumnIndex 和 end控制
     *
     * @param sheet
     * @param beginRowIndex    开始 join 的row index
     * @param endRowIndex      结束join 的 row index
     * @param insertRowIndex   拟插入的行号
     * @param beginColumnIndex 开始的列号
     * @param endColumnIndex   结束的列号
     */
    static public void insertRowAndJoinCellContentOfTheSameColumn(Sheet sheet, Integer insertRowIndex, Integer beginRowIndex, Integer endRowIndex, Integer beginColumnIndex, Integer endColumnIndex) {
        sheet.shiftRows(insertRowIndex, sheet.getLastRowNum(), 1, true, false); // 在目标行插入一行
        Row row = sheet.createRow(insertRowIndex); // 创建新插入的行
        int maxHeadColumnIndex = 19;
        List<String> fillContentList = new ArrayList<>();
        for (int i = beginColumnIndex; i <= endColumnIndex; i++) {
            String fillContent = "";
            for (Integer integer = beginRowIndex; integer <= endRowIndex; integer++) {
                fillContent += "_" + sheet.getRow(integer).getCell(i).getStringCellValue();
            }
            fillContent = fillContent.substring(1, fillContent.length());
            fillContentList.add(fillContent);
        }

        int j = 0;
        for (int i = beginColumnIndex; i <= endColumnIndex; i++) {
            Cell cell = row.createCell(i);
            cell.setCellValue(fillContentList.get(j++));
        }

    }

    /**
     * 将cell 的值 转换成 string
     *
     * @param workbook
     * @param cell
     * @return
     * @throws ParseException
     */
    public static String getCellValueByCell(Workbook workbook, Cell cell) throws ParseException {
        FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
        //处理科学计数法
        NumberFormat nf = NumberFormat.getInstance();
        //判断是否为null或空串
        if (cell == null || cell.toString().trim().equals("")) {
            return "";
        }
        String cellValue = "";
        CellType cellType = cell.getCellType();
        if (cellType == CellType.FORMULA) {
            cellType = evaluator.evaluate(cell).getCellType();
        }
        switch (cellType) {
            //字符串类型
            case STRING:
                cellValue = cell.getStringCellValue().trim();
                cellValue = StrUtil.isEmpty(cellValue) ? "" : cellValue;
                break;
            //数值类型
            case NUMERIC:
//                System.out.println("test1");
//                System.out.println(isCellDateFormatted(cell));
//                if (DateUtil.isCellDateFormatted(cell)) {
//                    System.out.println("单元格包含日期或时间: " + cell.getDateCellValue());
//                } else {
//                    System.out.println("单元格包含数值: " + cell.getNumericCellValue());
//                }

                //是否是日期格式
                if (isCellDateFormatted(cell)) {
                    String format1 = "M/d/yy";
                    String format2 = "yyyy-MM-dd";
                    DataFormatter dataFormatter = new DataFormatter(Locale.getDefault());
                    String formattedValue = dataFormatter.formatCellValue(cell);
                    cellValue = NslUtilDateTime.convertDateString(formattedValue, format1, format2);
                } else {
                    cellValue = String.valueOf(nf.format(cell.getNumericCellValue()));

                    //去除NumberFormat生成的,符号
                    cellValue = cellValue.replace(",", "");
                    cellValue = StrUtil.isEmpty(cellValue) ? "" : cellValue;
                }

                break;
            //其它类型:错误类型怎么处理
            default:
                cellValue = "";
                break;
        }
        return cellValue;
    }

    /**
     * cell 是否是 日期格式
     *
     * @param cell
     * @return
     */
    public static boolean isCellDateFormatted(Cell cell) {
        if (cell == null) {
            return false;
        }

        // 使用DataFormatter来格式化单元格
        //当日期为日期 2022/12/1  formattedValue=12/1/22
        DataFormatter dataFormatter = new DataFormatter(Locale.getDefault());
        String formattedValue = dataFormatter.formatCellValue(cell);

        return NslUtilDateTime.isValidDate(formattedValue);
    }


    /**
     * 根据 Sheet 名称获取行数
     */
    public static int getSheetRowCount(String filePath, String sheetName) throws IOException {
        FileInputStream file = new FileInputStream(new File(filePath));
        Workbook workbook = WorkbookFactory.create(file); // 自动识别 .xls 和 .xlsx
        Sheet sheet = workbook.getSheet(sheetName);
        int rowCount = sheet.getPhysicalNumberOfRows(); // 获取实际占用的行数
        workbook.close();
        file.close();
        return rowCount;
    }

    /**
     * 根据 Sheet 索引获取行数
     */
    public static int getSheetRowCount(String filePath, int sheetIndex) throws IOException {
        FileInputStream file = new FileInputStream(new File(filePath));
        Workbook workbook = WorkbookFactory.create(file); // 自动识别 .xls 和 .xlsx
        Sheet sheet = workbook.getSheetAt(sheetIndex);
        int rowCount = sheet.getPhysicalNumberOfRows(); // 获取实际占用的行数
        workbook.close();
        file.close();
        return rowCount;
    }

    /**
     * 在指定表格中，定位 关键词的位置
     *
     * @param keyWordSet
     * @param excelTemplate
     * @param endRowIndex    查询表的行范围，比这个小的行号里面要查询
     * @param endColumnIndex 查询表的列范围，比这个小的列号里面要查询
     * @return
     */
    public static Map<String, NslPointInt> findKeyWordCoordinate(Set<String> keyWordSet, ExcelTemplate excelTemplate, int endRowIndex, int endColumnIndex) {

        ExcelStringRowTable excelStringRowTable = NslUtilExcelRead.readDataAsLineListOfStringListIncludeHeadRows(excelTemplate, endColumnIndex);


        return findKeyWordCoordinate(keyWordSet, excelStringRowTable, endRowIndex, endColumnIndex);
    }

    /**
     * 在指定表格中，定位 关键词的位置
     *
     * @param keyWordSet
     * @param excelStringRowTable 读取表格的内容
     * @param endRowIndex         查询表的行范围，比这个小的行号里面要查询
     * @param endColumnIndex      查询表的列范围，比这个小的列号里面要查询
     * @return
     */
    public static Map<String, NslPointInt> findKeyWordCoordinate(Set<String> keyWordSet, ExcelStringRowTable excelStringRowTable, int endRowIndex, int endColumnIndex) {
        Map<String, NslPointInt> xyMap = new HashMap<>();

        List<ExcelStringRowTable.ExcelStringRow> rowList = excelStringRowTable.getRowList();

        int rowSize = endRowIndex > rowList.size() ? rowList.size() : endRowIndex;

        for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
            ExcelStringRowTable.ExcelStringRow stringRow = rowList.get(rowIndex);
            List<String> stringList = stringRow.getStringList();
            int columnSize = endColumnIndex > stringList.size() ? stringList.size() : endColumnIndex;
            for (int columnIndex = 0; columnIndex < columnSize; columnIndex++) {
                String cellString = stringList.get(columnIndex);
                for (String keyword : keyWordSet) {
                    if (cellString.equals(keyword)) {
                        NslPointInt pointInt = new NslPointInt();
                        pointInt.setX(columnIndex);
                        pointInt.setY(rowIndex);
                        xyMap.put(keyword, pointInt);
                    }
                }
            }
        }
        return xyMap;
    }

    /**
     * 根据指定的keyword set 和 表头占用行数，先查找表头中的关键词所在的列编号，再将关键词所在列的每一行信息存入map,所有行组成list
     * 行 Map中： key=keyword value=cell content
     *
     * @param keyWordSet          关键词set
     * @param excelStringRowTable 表中所有行信息，包含表头
     * @param headRowCount        表头占用行数
     * @param endColumnIndex      读取表格最大的列编号，大于此编号的不读取
     * @return
     */
    public static List<Map<String, String>> findKeyWordColumnContentMapList(Set<String> keyWordSet, ExcelStringRowTable excelStringRowTable, int headRowCount, int endColumnIndex) {
        Map<String, NslPointInt> keyWordMap = findKeyWordCoordinate(keyWordSet, excelStringRowTable, headRowCount, endColumnIndex);

        int beginRow = headRowCount;
        List<Map<String, String>> rowMapList = new ArrayList<>();

        List<ExcelStringRowTable.ExcelStringRow> rowList = excelStringRowTable.getRowList();

        for (int i = beginRow; i < rowList.size(); i++) {
            ExcelStringRowTable.ExcelStringRow stringRow = rowList.get(i);
            Map<String, String> keywordRowMap = new HashMap<>();
            for (String keyword : keyWordSet) {
                int contentColumnIndex = keyWordMap.get(keyword).getX();
                String content = stringRow.getStringList().get(contentColumnIndex);
                keywordRowMap.put(keyword, content);
            }
            rowMapList.add(keywordRowMap);
        }
        return rowMapList;
    }

    /**
     * byte errorCode = cell.getErrorCellValue();
     * 根据错误代码返回错误信息
     */
    public static String getErrorMessage(byte errorCode) {
        switch (errorCode) {
            case 0x00:
                //- 引用两个不相交的区域
                return "#NULL!";
            case 0x07:
                // - 除以零
                return "#DIV/0!";
            case 0x0F:
                // - 参数类型错误
                return "#VALUE!";
            case 0x17:
                // - 无效的单元格引用
                return "#REF!";
            case 0x1D:
                // - 未定义的名称
                return "#NAME?";
            case 0x24:
                // - 无效的数字
                return "#NUM!";
            case 0x2A:
                // - 数据不可用
                return "#N/A";
            default:
                return "未知错误代码";
        }
    }


    /**
     * 返回cell 原始内容
     * @param cell
     * @return
     */
    static public CellTypeAndValue getCellTypeAndValue(Cell cell) {
        if (cell == null) {
            throw new RuntimeException("cell is null");
        }
        CellType cellType = cell.getCellType();
        switch (cellType) {
            case STRING:
                return new CellTypeAndValue(CellType.STRING, cell.getStringCellValue());
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    return new CellTypeAndValue(cellType, cell.getDateCellValue());
                } else {
                    return new CellTypeAndValue(cellType, Double.valueOf(cell.getNumericCellValue()));
                }
            case BOOLEAN:
                return new CellTypeAndValue(cellType, cell.getBooleanCellValue());
            case FORMULA:
                return new CellTypeAndValue(cellType, cell.getCellFormula());
            case BLANK:
                return new CellTypeAndValue(cellType, null);
            case ERROR:
                return new CellTypeAndValue(cellType, cell.getErrorCellValue());
            default:
                throw new RuntimeException("cell 类型不可知");
        }
    }

    /**
     * 返回cell内容，以string 或者 数字形式返回
     * 数字形式为 Double
     * 日期时间 以字符串形式返回
     * @param cell
     * @param whetherCalFormula 是否计算公式
     * @return
     */
    static public Object getCellValueAsStringOrNumber(Cell cell,FormulaEvaluator evaluator,boolean whetherCalFormula){
        CellTypeAndValue cellTypeAndValue=getCellTypeAndValue(cell);
        Object cellValue=cellTypeAndValue.getCellValue();
        CellType cellType = cellTypeAndValue.getCellTypeEnum();
        switch (cellType) {
            case STRING:
                return cellValue;
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    Date date=cell.getDateCellValue();
                    return NslUtilDateTime.formatAsYyyyMmDdHhMmSs(date);
                } else {
                    return  cellValue;
                }
            case BOOLEAN:
                return cellValue+"";
            case FORMULA:
                if(whetherCalFormula){
                    return NslUtilExcelFormula.calFormulaCellValueAsStringOrNumber(cell,evaluator);
                }else{
                    return cell.getCellFormula();
                }
            case BLANK:
                return "";
            case ERROR:
                return getErrorMessage((Byte) cellValue);
            default:
                throw new RuntimeException("cell 类型不可知");
        }
    }

    /**
     * 返回cell内容，以string 或者 数字形式返回, 计算公式
     * 数字形式为 Double
     * 日期时间 以字符串形式返回
     * @param cell
     * @return
     */
    public static Object getCellValueAsStringOrNumber(Cell cell,FormulaEvaluator evaluator){
        return getCellValueAsStringOrNumber(cell,evaluator,true);
    }

    /**
     * 返回cell内容，以string 或者 数字形式返回, 计算公式
     * 数字形式为 Double
     * 日期时间 以字符串形式返回
     * @param cell
     * @return
     */
    public static Object getCellValueAsString(Cell cell,FormulaEvaluator evaluator,boolean whetherCalFormula){
        return getCellValueAsStringOrNumber(cell,evaluator,whetherCalFormula).toString();
    }

    /**
     * 返回cell内容，以string 或者 数字形式返回, 计算公式
     * 数字形式为 Double
     * 日期时间 以字符串形式返回
     * @param cell
     * @return
     */
    public static Object getCellValueAsString(Cell cell,FormulaEvaluator evaluator){
        return getCellValueAsStringOrNumber(cell,evaluator).toString();
    }

}





